package com.wcs.exam.common.base.page;

import com.baomidou.mybatisplus.core.metadata.IPage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 分页
 *
 * @author wcs
 */
public final class PageUtil {

    /**
     * 默认每页记录数(20)
     */
    public static final int DEFAULT_PAGE_SIZE = 20;
    /**
     * 最大每页记录数(1000)
     */
    public static final int MAX_PAGE_SIZE = 1000;
    private static final Logger logger = LoggerFactory.getLogger(PageUtil.class);
    private static final Pattern ORDER_BY_PATTERN = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*", Pattern.CASE_INSENSITIVE);

    private PageUtil() {
    }

    public static <T extends Serializable> Page<T> transform(IPage<?> page, Class<T> classType) {
        Page<T> pb = new Page<>();
        try {
            pb.setPageCurrent((int) page.getCurrent());
            pb.setPageSize((int) page.getSize());
            pb.setTotalCount((int) page.getTotal());
            pb.setTotalPage((int) page.getPages());
            pb.setList(copyList(page.getRecords(), classType));
        } catch (Exception e) {
            logger.error("transform error", e);
        }
        return pb;
    }

    public static <T extends Serializable> Page<T> transform(Page<?> page, Class<T> classType) {
        Page<T> pb = new Page<>();
        try {
            pb.setPageCurrent(page.getPageCurrent());
            pb.setPageSize(page.getPageSize());
            pb.setTotalCount(page.getTotalCount());
            pb.setTotalPage(page.getTotalPage());
            pb.setList(copyList(page.getList(), classType));
        } catch (Exception e) {
            logger.error("transform error", e);
        }
        return pb;
    }

    /**
     * @param source 源数据
     * @param clazz  目标数据类型
     * @return List
     */
    public static <T> List<T> copyList(List<?> source, Class<T> clazz) {
        if (source == null || source.isEmpty()) {
            return Collections.emptyList();
        }
        List<T> res = new ArrayList<>(source.size());
        for (Object o : source) {
            try {
                T t = clazz.getDeclaredConstructor().newInstance();
                res.add(t);
                BeanUtils.copyProperties(o, t);
            } catch (Exception e) {
                logger.error("copyList error", e);
            }
        }
        return res;
    }

    /**
     * 计算总页数
     *
     * @param total 总记录数.
     * @param size  每页记录数.
     */
    public static long countTotalSize(final long total, final long size) {
        if (total % size == 0) {
            // 刚好整除
            return total / size;
        } else {
            // 不能整除则总页数为：商 + 1
            return total / size + 1;
        }
    }

    /**
     * 校验当前页数current<br/>
     * 1、先根据总记录数total和每页记录数size，计算出总页数totalSize<br/>
     * 2、判断页面提交过来的当前页数current是否大于总页数totalSize，大于则返回totalSize<br/>
     * 3、判断current是否小于1，小于则返回1<br/>
     * 4、其它则直接返回current
     *
     * @param total   要分页的总记录数
     * @param size    每页记录数大小
     * @param current 输入的当前页数
     * @return pageCurrent
     */
    public static long checkCurrent(long total, long size, long current) {
        // 最大页数
        long totalPage = countTotalSize(total, size);
        if (current > totalPage) {
            // 如果页面提交过来的页数大于总页数，则将当前页设为总页数
            // 此时要求totalPage要大于获等于1
            if (totalPage < 1) {
                return 1;
            }
            return totalPage;
        } else if (current < 1) {
            // 当前页不能小于1（避免页面输入不正确值）
            return 1;
        } else {
            return current;
        }
    }

    /**
     * 校验页面输入的每页记录数pageSize是否合法<br/>
     * 1、当页面输入的每页记录数pageSize大于允许的最大每页记录数MAX_PAGE_SIZE时，返回MAX_PAGE_SIZE 2、如果size小于1，则返回默认的每页记录数DEFAULT_PAGE_SIZE
     *
     * @param size 页面输入的每页记录数
     * @return checkPageSize
     */
    public static long checkSize(long size) {
        if (size > MAX_PAGE_SIZE) {
            return MAX_PAGE_SIZE;
        } else if (size < 1) {
            return DEFAULT_PAGE_SIZE;
        } else {
            return size;
        }
    }

    /**
     * 根据总记录数，对页面传来的分页参数进行校验，并返分页的SQL语句
     *
     * @param total   总记录数
     * @param current 当前页
     * @param size    每页记录数
     * @return limitSql
     */
    public static String limitSql(long total, long current, long size) {
        // 校验当前页数
        current = checkCurrent(total, size, current);
        // 校验每页记录数
        size = checkSize(size);
        return " limit " + countOffset(current, size) + "," + size;
    }

    /**
     * 根据分页查询的SQL语句，获取统计总记录数的语句
     *
     * @param sql 分页查询的SQL
     * @return countSql
     */
    public static String countSql(String sql) {
        // 去除第一个from前的内容
        String countSql = sql.substring(sql.toLowerCase().indexOf("from"));
        return "select count(*) " + removeOrderBy(countSql);
    }

    /**
     * 移除SQL语句中的的order by子句（用于分页前获取总记录数，不需要排序）
     *
     * @param sql 原始SQL
     * @return 去除order by子句后的内容
     */
    private static String removeOrderBy(String sql) {
        Matcher mc = ORDER_BY_PATTERN.matcher(sql);
        StringBuilder strBuf = new StringBuilder();
        while (mc.find()) {
            mc.appendReplacement(strBuf, "");
        }
        mc.appendTail(strBuf);
        return strBuf.toString();
    }

    /**
     * 计算当前分页的开始记录的索引
     *
     * @param current 当前第几页
     * @param size    每页记录数
     * @return 当前页开始记录号
     */
    private static long countOffset(final long current, final long size) {
        return (current - 1) * size;
    }

}
